package gek.uni.diplom.ocl.example.commands;

import gek.uni.diplom.ecore.stack.Stack.Element;
import gek.uni.diplom.ecore.stack.Stack.Stack;
import gek.uni.diplom.ecore.stack.Stack.StackFactory;
import gek.uni.diplom.ecore.stack.Stack.StackPackage;
import gek.uni.diplom.ocl.example.exceptions.ErrorException;
import gek.uni.diplom.ocl.example.exceptions.NotImplementedMethodException;
import gek.uni.diplom.ocl.example.exceptions.OperationNotFoundException;
import gek.uni.diplom.ocl.example.generators.visitors.hierarhical.PatternGeneratorVisitor;
import gek.uni.diplom.ocl.example.patterns.hierarhical.CodeCompleter;
import gek.uni.diplom.ocl.example.patterns.hierarhical.IPattern;
import gek.uni.diplom.ocl.example.visitors.EcoreTrueFalseUtils;
import gek.uni.diplom.ocl.example.visitors.OpenBracketVisitor;
import gek.uni.diplom.ocl.example.visitors.SimplifierVisitor;

import java.util.Collection;
import java.util.Iterator;
import java.util.List;
import java.util.Set;

import org.eclipse.core.commands.ExecutionEvent;
import org.eclipse.core.commands.ExecutionException;
import org.eclipse.core.commands.IHandler;
import org.eclipse.core.commands.IHandlerListener;
import org.eclipse.emf.ecore.EClass;
import org.eclipse.emf.ecore.EClassifier;
import org.eclipse.emf.ecore.EEnumLiteral;
import org.eclipse.emf.ecore.EObject;
import org.eclipse.emf.ecore.EOperation;
import org.eclipse.emf.ecore.EPackage;
import org.eclipse.emf.ecore.EParameter;
import org.eclipse.emf.ecore.EStructuralFeature;
import org.eclipse.ocl.OCL;
import org.eclipse.ocl.ParserException;
import org.eclipse.ocl.Query;
import org.eclipse.ocl.ecore.CallOperationAction;
import org.eclipse.ocl.ecore.Constraint;
import org.eclipse.ocl.ecore.EcoreEnvironmentFactory;
import org.eclipse.ocl.ecore.SendSignalAction;
import org.eclipse.ocl.expressions.OCLExpression;
import org.eclipse.ocl.helper.OCLHelper;

public class TempCommandHandler implements IHandler {

  @Override
  public void addHandlerListener(IHandlerListener handlerListener) {}

  @Override
  public void dispose() {}

  @Override
  public Object execute(ExecutionEvent event) throws ExecutionException {
    // myActions();
    // myConvertAction();
    myConsvertElementAction();
    return null;
  }

  @Override
  public boolean isEnabled() {
    return true;
  }

  @Override
  public boolean isHandled() {
    return true;
  }

  @Override
  public void removeHandlerListener(IHandlerListener handlerListener) {

  }

  private void myActions() {
    OCL<EPackage, EClassifier, EOperation, EStructuralFeature, EEnumLiteral, EParameter, EObject, CallOperationAction, SendSignalAction, Constraint, EClass, EObject> ocl;
    ocl = OCL.newInstance(EcoreEnvironmentFactory.INSTANCE);

    OCLHelper<EClassifier, EOperation, ?, Constraint> helper =
        ocl.createOCLHelper();

    List<EOperation> operations = StackPackage.Literals.STACK.getEOperations();
    EOperation resOp = null;
    for (EOperation op : operations) {
      if (op.getName().equals("pop")) resOp = op;
    }
    if (resOp == null) return;

    helper.setOperationContext(StackPackage.Literals.STACK, resOp);

    try {
      Constraint constraint =
          helper.createBodyCondition("self.children->at(1)");
      Query<EClassifier, EClass, EObject> queryEval =
          ocl.createQuery(constraint);
      Stack stack = StackFactory.eINSTANCE.createStack();
      Element el = StackFactory.eINSTANCE.createElement();
      el.setVal(5);
      stack.getChildren().add(el);
      Object obj = queryEval.evaluate(stack);
      System.out.println(obj);

    } catch (ParserException e) {
      System.out.println(e.getMessage());
      // TODO Auto-generated catch block
      // e.printStackTrace();
    }
  }

  /**
   * Попытка проинициализировать объект стек для проверки ограничения(self->size
   * > 0).
   * 
   */
  private void myConvertAction() {
    String expression = "self.size > 0";
    OCL<EPackage, EClassifier, EOperation, EStructuralFeature, EEnumLiteral, EParameter, EObject, CallOperationAction, SendSignalAction, Constraint, EClass, EObject> ocl;
    ocl = OCL.newInstance(EcoreEnvironmentFactory.INSTANCE);

    OCLHelper<EClassifier, EOperation, EStructuralFeature, Constraint> helper =
        ocl.createOCLHelper();

    List<EOperation> operations = StackPackage.Literals.STACK.getEOperations();
    EOperation resOp = null;
    for (EOperation op : operations) {
      if (op.getName().equals("push")) resOp = op;
    }
    if (resOp == null) return;

    helper.setOperationContext(StackPackage.Literals.STACK, resOp);

    try {
      // expression = "self.size+5-14 > 0+self.size";
      // expression = "14*(self.size+5+7)";
      // expression = "14*(5 + 3*(4 + 2*self.size))";
      // expression = "5*(4-3*(2-1*(44*self.size)))";
      // expression = "5.2-44*(1-self.size) = 5+3*self.size";
      // expression = "5*(self.size -1)> 5 and self.size > 6";
      // expression = "'LaLa'.oclIsTypeOf(String)";
      // expression = "5 = 6";
      expression = "element.val = 5";
      // expression = "element.oclIsUndefined()";
      // expression = "self.size - 5 = 0";
      // expression =
      // "self.children->select(child | child.val = 5)->first().oclIsUndefined()";
      // expression = "true";

      OCLExpression expression2 = helper.createQuery(expression);

      OpenBracketVisitor<EClassifier, EOperation, EStructuralFeature, EEnumLiteral, EParameter, EObject, CallOperationAction, SendSignalAction, Constraint> bracketOpener =
          new OpenBracketVisitor<EClassifier, EOperation, EStructuralFeature, EEnumLiteral, EParameter, EObject, CallOperationAction, SendSignalAction, Constraint>();
      OCLExpression<EClassifier> result = expression2.accept(bracketOpener);

      SimplifierVisitor<EClassifier, EOperation, EStructuralFeature, EEnumLiteral, EParameter, EObject, CallOperationAction, SendSignalAction, Constraint> simplifier =
          new SimplifierVisitor<EClassifier, EOperation, EStructuralFeature, EEnumLiteral, EParameter, EObject, CallOperationAction, SendSignalAction, Constraint>();
      OCLExpression<EClassifier> simplyResult = result.accept(simplifier);

      try {
        List<List<OCLExpression<EClassifier>>>[] res =
            EcoreTrueFalseUtils.getInstance().createTrueFalseExpressions(
                simplyResult);
        printInequalitySystem(res[0],
            "Неравенства, соблюдая которые, результат будет True");

        printInequalitySystem(res[1],
            "Неравенства, соблюдая которые, результат будет False");
      } catch (NotImplementedMethodException e) {
        e.printStackTrace();
      } catch (OperationNotFoundException e) {
        e.printStackTrace();
      } catch (ErrorException e) {
        e.printStackTrace();
      }
    } catch (ParserException e) {
      System.out.println(e.getMessage());
    }
  }

  private void myConsvertElementAction() {
    OCL<EPackage, EClassifier, EOperation, EStructuralFeature, EEnumLiteral, EParameter, EObject, CallOperationAction, SendSignalAction, Constraint, EClass, EObject> ocl;
    ocl = OCL.newInstance(EcoreEnvironmentFactory.INSTANCE);

    OCLHelper<EClassifier, EOperation, EStructuralFeature, Constraint> helper =
        ocl.createOCLHelper();

    List<EOperation> operations =
        StackPackage.Literals.ELEMENT.getEAllOperations();// .getEOperations();
    EOperation resOp = null;
    for (EOperation op : operations) {
      if (op.getName().equals("setVal")) resOp = op;
    }
    if (resOp == null) return;

    helper.setOperationContext(StackPackage.Literals.ELEMENT, resOp);

    String expression = null;
    try {
       expression = "self.stack.children->size()=2";
//      expression = "self.stack.size -6 = 0";
//      expression ="self.stack.children->size() > 0";
//      expression = "self.stack.oclIsUndefined() = true";

      OCLExpression expression2 = helper.createQuery(expression);

      OpenBracketVisitor<EClassifier, EOperation, EStructuralFeature, EEnumLiteral, EParameter, EObject, CallOperationAction, SendSignalAction, Constraint> bracketOpener =
          new OpenBracketVisitor<EClassifier, EOperation, EStructuralFeature, EEnumLiteral, EParameter, EObject, CallOperationAction, SendSignalAction, Constraint>();
      OCLExpression<EClassifier> result = expression2.accept(bracketOpener);

      SimplifierVisitor<EClassifier, EOperation, EStructuralFeature, EEnumLiteral, EParameter, EObject, CallOperationAction, SendSignalAction, Constraint> simplifier =
          new SimplifierVisitor<EClassifier, EOperation, EStructuralFeature, EEnumLiteral, EParameter, EObject, CallOperationAction, SendSignalAction, Constraint>();
      OCLExpression<EClassifier> simplyResult = result.accept(simplifier);

      try {
        List<List<OCLExpression<EClassifier>>>[] res =
            EcoreTrueFalseUtils.getInstance().createTrueFalseExpressions(
                simplyResult);
        printInequalitySystem(res[0],
            "Неравенства, соблюдая которые, результат будет True");

        PatternGeneratorVisitor visitor = new PatternGeneratorVisitor();
        for (Iterator<OCLExpression<EClassifier>> it = res[0].get(0).iterator(); it
            .hasNext(); ) {
          OCLExpression<EClassifier> exp = it.next();
          IPattern pattern = exp.accept(visitor);
          
          Set<String> collectImportString = CodeCompleter.collectImportString(pattern);
          Collection<String> testCode = CodeCompleter.collectTestCode(pattern);
          
          System.out.println(collectImportString.size());
        }
      } catch (NotImplementedMethodException e) {
        e.printStackTrace();
      } catch (OperationNotFoundException e) {
        e.printStackTrace();
      } catch (ErrorException e) {
        e.printStackTrace();
      }
    } catch (ParserException e) {
      System.out.println(e.getMessage());
    }

  }

  private void printInequalitySystem(
      List<List<OCLExpression<EClassifier>>> system, String prefix) {
    System.out.println(prefix);
    for (List<OCLExpression<EClassifier>> trueList : system) {
      for (OCLExpression<EClassifier> exp : trueList) {
        System.out.println("\t" + exp.toString());
      }
      System.out.println("----------------------");
    }
  }
}
